Composite Pattern

Composite Pattern ကတော့ structural design pattern ဖြစ်ပါတယ်။ Composite က Tree Structure တွေ မှာ အသုံးများပါတယ်။ Tree Structure ပြဿနာတွေ ဆိုရင် Composite pattern ကို အသုံးပြုနိုင်ပါတယ်။

UML Diagram အတိုင်းပါပဲ Composite ထဲမှာ Component တွေ ပါပါတယ်။ Component တွေ ဖြစ်သည့် အတွက် Child Leaf လည်း ဖြစ်နိုင်သလို ကိုယ်တိုင် Composite class လည်း ဖြစ်နိုင်ပါတယ်။ တနည်းပြောရရင် File structure လိုမျိုး Folder အောက်မှာ Folder လည်း ဖြစ်နိုင်သလို File လည်း ဖြစ်နိုင်ပါတယ်။ Linux က file system တွေမှာ ဆိုရင် root(/) အောက်မှာ အခါ folder နဲ့ file တွေ ရှိပြီး အဆင့်ဆင့် ခွဲ သွားတာ မျိုးပေါ့။

/
/var/
/var/www/
/var/www/html/
/var/www/html/index.html
/var/www/html/hello.html

Composite Pattern မသုံးပဲ ပုံမှန် ရေးမယ် ဆိုရင် Folder Strcuture အတွက် UML က အောက်က ပုံ အတိုင်း ဖြစ်မှာပါ။

ဒီ Class Diagram မှာ အဓိ ပြဿနာက File အပြင် short cut ထပ်ဖြည့်မယ်ဆိုရင် Directory ထဲမှာ ထပ်ပြင်ရမယ်။ Open Close Principle နဲ့ မကိုက်တော့ဘူး။ တစ်ခု ထပ်လာတိုင်း တစ်ခါ ထပ်ပြင်နေရမှာပါ။ အဲဒီ လို ပြဿနာတွေ အတွက် Composite ကို သုံးပါတယ်။ Composite Pattern ကတော့ အောက်ကလို Clas Diagram ပါ။

Directory ဟာ Component Interface ကို Implement လုပ်ထားပြီး Directory ထဲ မှာ Component ထပ်ပါသေးတယ် လို့ ပြထားပါတယ်။

Code ကို ကြည့်ရအောင်။

FileComponent.java

public abstract class FileComponent { 

    protected String name;

    public FileSystemComponent(String name) { 
        this.name = name;
    }
    
    public abstract void print();
    
    public abstract int getSize(); 
 
}

File.java

public class File extends FileComponent { 

    private int sizeInBytes;

    public File(String name, int sizeInBytes) { 
        super(name);
        this.sizeInBytes = sizeInBytes;
    }
    
    public int getSize() { 
        return sizeInBytes;
    }
    
    public void print() {
        System.out.println("--- file " + name + " size=" + getSize() + " bytes");
    } 
}

Directory.java

public class Directory extends FileComponent {
    protected Collection<FileSystemComponent> fileSystemComponents = new ArrayList<FileSystemComponent>();

    public Directory(String name) { 
        super(name);
    }
    
    public void addComponent(FileSystemComponent component) { 
        fileSystemComponents.add(component);
    }
    
    public int getSize() {
        int sizeInBytes = 0;
        for (FileSystemComponent component : fileSystemComponents) {
            sizeInBytes += component.getSize(); 
        }
        return sizeInBytes;
  }
  
    public void print() {
        System.out.println("-- dir " + name + " size=" + getSize() + " bytes"); 
        
        for (FileSystemComponent component : fileSystemComponents) {
            component.print(); 
        }
    } 
}

ဒီ code မှာ ဆိုရင် Directory အောက်မှာ File လည်း ရှိနိုင်သလို Directory လည်း ရှိနိုင်ပါတယ်။ အဲဒီ အတွက် getSize() ကို ခေါ်လိုက်ရင် child component တွေ ရဲ့ getSize() ကို ဆင့်ကာ ဆင့်ကာ ခေါ်ပြီး File Size ကို ရနိုင်ပါတယ်။

Pros and Cons

ကောင်းတာကတော့ Complex ဖြစ်သည့် Tree Structure တွေကို ရှင်းရှင်းလင်းလင်း ဖြစ်သွားစေပါတယ်။ 

Open/Close Principle ကို လိုက်နာထားတယ်။ File အပြင် short cut ထပ်ဖြည့်လည်း Directory က ဘာမှ ထပ်ပြင်ဖို့ မလိုတော့ပါဘူး။

မကောင်းတာကတော့ တူညီသည့် Structure မရှိသည့် Component တွေမှာ interface တွေ အများကြီး ထပ်သုံးနေရလိမ့်မယ်။ ဥပမာ File မှာ getSize ရှိပေမယ့် ShotCut မှာ getSize မရှိသည့် အခါ။ နောက်ပြီး Vault Directory ဆိုပြီး ထပ်ဖြည့်သည့် အခါမှာ interface တွေ ထပ်ပေါင်း ထည့်ရပြီး interface တွေ အများကြီး ဖြစ်သွားနိုင်ပါတယ်။